package controllers

import config.{Global, MyAwait, MyFile}
import dao.{BlockDao, BlockRangeDao, GenomeDao}

import javax.inject.Inject
import models.Tables.{BlockRow, BlockrangeRow, GenomeRow}
import org.apache.commons.io.FileUtils
import play.api.libs.json.Json
import play.api.mvc.{AbstractController, ControllerComponents}
import utils.{TableUtils, Utils}

import java.util.Properties
import scala.collection.mutable
import scala.jdk.CollectionConverters._

class InsertController @Inject()(genomeDao: GenomeDao,
                                 blockDao: BlockDao,
                                 blockRangeDao: BlockRangeDao,
                                 cc: ControllerComponents) extends AbstractController(cc) with MyFile with MyAwait {

  def insert(insert: String) = Action {
    insert match {
      case "insertGenome" => insertGenome
      case "insertZM4" => insertZM4
      case "insertBlock" => insertBlock
      case "insertBlockRange" => insertBlockRange
      case "getZmnoSnp" => getZmnoSnp
      case "getNetWork1" => getNetWork1
      case "getNetWork2" => getNetWork2
      case "getSnpFreq" => getSnpFreq
      case "getBlockRange" => getBlockRange
    }
    Ok(Json.toJson("success"))
  }

  def insertGenome = {
    val path = "D:\\紫花苜蓿数据库\\gff"

    val annos = "D:\\紫花苜蓿数据库\\annotations".listFile.map { x =>
      val name = x.getName.split('.').head.toUpperCase
      name -> x.readLines.map(_.split("\t").map(_.trim)).map { y =>
        val gene = y.head
        gene -> y.drop(4)
      }.toMap
    }.toMap


    path.listFile.foreach { x =>
      val name = x.getName.split('.').head.toUpperCase
      println(name)
      val anno = annos(name)
      val geneToRna = x.readLines.filter(!_.startsWith("#")).map(_.split("\t")).filter(_(2) == "mRNA").map { line =>
        val l = line.last.split(";")
        val rnaId = l.find(_.startsWith("ID=")).get.drop(3)
        val geneID = l.find(_.startsWith("Parent=")).get.drop(7)
        geneID -> rnaId
      }.toMap

      val buffer = x.readLines.filter(!_.startsWith("#")).map(_.split("\t")).filter(_(2) == "gene").map { line =>
        val geneId = line.last.split(";").head.drop(3)
        val chr = line.head
        val start = line(3).toInt
        val end = line(4).toInt
        val strand = line(6)
        val rnaId = geneToRna(geneId)
        val a = anno.getOrElse(rnaId, Array("-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"))
        val eggnogOgs = a.head
        val narrOgName = a(1)
        val narrOgCat = a(2)
        val narrOgDesc = a(3)
        val bestOgName = a(4)
        val bestOgCat = a(5)
        val bestOgDesc = a(6)
        val preferredName = a(7)
        val gos = a(8)
        val ec = a(9)
        val keggKo = a(10)
        val keggPathway = a(11)
        val keggModule = a(12)
        val keggReaction = a(13)
        val keggRclass = a(14)
        val brite = a(15)
        val keggTc = a(16)
        val cazy = a(17)
        val biggReaction = a(18)
        val pfams = a(19)

        GenomeRow(0, geneId, name, chr, start, end, strand, eggnogOgs, narrOgName, narrOgCat, narrOgDesc, bestOgName,
          bestOgCat, bestOgDesc, preferredName, gos, ec, keggKo, keggPathway, keggModule, keggReaction, keggRclass, brite, keggTc,
          cazy, biggReaction, pfams)
      }
      genomeDao.insert(buffer.toSeq).toAwait
    }
  }

  def insertZM4 = {
    val path = "H:\\紫花苜蓿数据库\\2023_3_13\\wangzhang2023.03.011\\zm4"


    val name = "ZM-4"
    val anno = s"$path/ZM4.emapper.annotations".readLines.filter(!_.startsWith("#")).map(_.split("\t").map(_.trim)).map { y =>
      val gene = y.head
      gene -> y.drop(4)
    }.toMap

    val buffer = s"$path/zm-4.gene.gff3".readLines.filter(!_.startsWith("#")).map(_.split("\t")).filter(_(2) == "gene").map { line =>
      val geneId = line.last.split(";").head.drop(3)
      val chr = line.head
      val start = line(3).toInt
      val end = line(4).toInt
      val strand = line(6)

      val a = anno.getOrElse(geneId, Array("-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"))
      val eggnogOgs = a.head
      val narrOgName = "-"
      val narrOgCat = "-"
      val narrOgDesc = "-"
      val bestOgName = "-"
      val bestOgCat = "-"
      val bestOgDesc = "-"
      val preferredName = "-"
      val gos = a(5)
      val ec = a(6)
      val keggKo = a(7)
      val keggPathway = a(8)
      val keggModule = a(9)
      val keggReaction = a(10)
      val keggRclass = a(11)
      val brite = a(12)
      val keggTc = a(13)
      val cazy = a(14)
      val biggReaction = a(15)
      val pfams = a(16)

      GenomeRow(0, geneId, name, chr, start, end, strand, eggnogOgs, narrOgName, narrOgCat, narrOgDesc, bestOgName,
        bestOgCat, bestOgDesc, preferredName, gos, ec, keggKo, keggPathway, keggModule, keggReaction, keggRclass, brite, keggTc,
        cazy, biggReaction, pfams)
    }

    // buffer.foreach(println)
    genomeDao.insert(buffer.toSeq).toAwait

  }


  def insertBlock = {
    val all = genomeDao.getAllGenome.toAwait.map { x => x.geneid -> x.id }.toMap
    val row = "D:\\紫花苜蓿数据库\\block".listFile.flatMap { x =>
      val line = x.readLines.filter(!_.startsWith("#")).map(_.split("\t")).map(y => y.head.split('.').init.mkString(".") -> y(1).split('.').init.mkString("."))
      line ++ line.map(y => y._2 -> y._1)
    }.groupBy(_._1).map { x =>
      all(x._1) -> BlockRow(all(x._1), x._1, x._2.map(_._2).distinct.mkString(","))
    }.toSeq.sortBy(_._1).map(_._2)
    blockDao.insert(row).toAwait
    Ok(Json.toJson("success!"))
  }

  def insertBlockRange = {
    val geneIdToId = genomeDao.getAllGenome.toAwait.map { x => x.geneid -> x.id }.toMap
    val time = System.currentTimeMillis()
    "D:\\紫花苜蓿数据库\\block".listFile.foreach { x =>
      println(x.getName)
      println(Global.getTime(time))
      val line = x.readFileToString
      val range = line.split("###").tail
      val row = range.zipWithIndex.map { case (y, i) =>
        val genes = y.trim.split("\n").map(_.split("\t")).map(y => y.head.split('.').init.mkString(".") -> y(1).split('.').init.mkString("."))
        val id1 = genes.map(g => geneIdToId(g._1))
        val gene1 = genomeDao.getByIds(id1).toAwait
        val chr1 = gene1.map(_.chr).distinct.head
        val start1 = gene1.map(_.start).min
        val end1 = gene1.map(_.end).max
        val len1 = end1 - start1 + 1
        val species1 = gene1.head.species
        val id2 = genes.map(g => geneIdToId(g._2))
        val gene2 = genomeDao.getByIds(id2).toAwait
        val chr2 = gene2.map(_.chr).distinct.head
        val start2 = gene2.map(_.start).min
        val end2 = gene2.map(_.end).max
        val len2 = end2 - start2 + 1
        val species2 = gene2.head.species
        BlockrangeRow(0, species1 + "_vs_" + species2 + "_" + (i + 1), chr1, species1, start1, end1, len1, chr2, species2, start2, end2, len2)
      }
      blockRangeDao.insert(row).toAwait
    }

    Ok(Json.toJson("success!"))
  }

  def getZmnoSnp = {
    val path = "H:\\紫花苜蓿数据库\\2023_9_22\\网站"

    genomeDao.getAllGenome.toAwait.filter(_.species == "ZMNO").groupBy(_.chr).foreach { z =>
      val chr = z._1
      println(chr)

      val snps = if (s"$path/snp/$chr.txt".toFile.exists()) {
        s"$path/snp/$chr.txt".readLines
      } else {
        mutable.Buffer("")
      }
      val snpMap = if (s"$path/snp/$chr.txt".toFile.exists()) {
        snps.map(_.split("\t")).zipWithIndex.map { y => (y._1(3).toInt, y._1(4).toInt, y._2) }
      } else {
        mutable.Buffer((0, 0, 0))
      }


      val indels = if (s"$path/indel/$chr.txt".toFile.exists()) {
        s"$path/indel/$chr.txt".readLines
      } else {
        mutable.Buffer("")
      }
      val indelMap = if (s"$path/indel/$chr.txt".toFile.exists()) {
        indels.map(_.split("\t")).zipWithIndex.map { y => (y._1(3).toInt, y._1(4).toInt, y._2) }

      } else {
        mutable.Buffer((0, 0, 0))
      }
      val svs = if (s"$path/sv/$chr.txt".toFile.exists()) {
        s"$path/sv/$chr.txt".readLines
      } else {
        mutable.Buffer("")
      }
      val svsMap = if (s"$path/sv/$chr.txt".toFile.exists()) {
        svs.map(_.split("\t")).zipWithIndex.map { y => (y._1(3).toInt, y._1(4).toInt, y._2) }
      } else {
        mutable.Buffer((0, 0, 0))
      }

      z._2.foreach { x =>
        val start = x.start
        val end = x.end
        val snpIndex = snpMap.filter(y => y._1 >= start && y._2 <= end).map(_._3)
        val indelIndex = indelMap.filter(y => y._1 >= start && y._2 <= end).map(_._3)
        val svIndex = svsMap.filter(y => y._1 >= start && y._2 <= end).map(_._3)


        if ((snpIndex ++ indelIndex ++ svIndex).nonEmpty) {
          var snp: mutable.Buffer[String] = mutable.Buffer()
          var indel: mutable.Buffer[String] = mutable.Buffer()
          var sv: mutable.Buffer[String] = mutable.Buffer()
          if (snpIndex.nonEmpty) {

            snp = snps.slice(snpIndex.min, snpIndex.max + 1).map(y => "SNP\t" + y)
          }

          if (indelIndex.nonEmpty) {


            indel = indels.slice(indelIndex.min, indelIndex.max + 1).map(y => "Indel\t" + y)
          }

          if (svIndex.nonEmpty) {


            sv = svs.slice(svIndex.min, svIndex.max + 1).map(y => "SV\t" + y)
          }

          FileUtils.writeLines(s"$path/gene/${x.geneid}.txt".toFile, (snp ++ indel ++ sv).asJava)
        }

      }


    }
    println("success")

  }


  def getZmnoSnp_old = {
    val path = "H:\\紫花苜蓿数据库\\2022_12_19\\bianyi"

    genomeDao.getAllGenome.toAwait.filter(_.species == "ZMNO").groupBy(_.chr).foreach { z =>
      val chr = z._1
      println(chr)

      val snps = s"$path/snp/$chr.txt".readLines
      val snpMap = snps.map(_.split("\t")).zipWithIndex.map { y => (y._1(1).toInt, y._1(2).toInt, y._2) }


      val indels = s"$path/indel/$chr.txt".readLines
      val indelMap = indels.map(_.split("\t")).zipWithIndex.map { y => (y._1(1).toInt, y._2) }

      val svs = s"$path/sv/$chr.txt".readLines
      val svsMap = svs.map(_.split("\t")).zipWithIndex.map { y => (y._1(1).toInt, y._1(2).toInt, y._2) }


      z._2.foreach { x =>
        val start = x.start
        val end = x.end
        val snpIndex = snpMap.filter(y => y._1 >= start && y._2 <= end).map(_._3)
        val indelIndex = indelMap.filter(y => y._1 >= start && y._1 <= end).map(_._2)
        val svIndex = svsMap.filter(y => y._1 >= start && y._2 <= end).map(_._3)


        if ((snpIndex ++ indelIndex ++ svIndex).nonEmpty) {
          var snp: mutable.Buffer[String] = mutable.Buffer()
          var indel: mutable.Buffer[String] = mutable.Buffer()
          var sv: mutable.Buffer[String] = mutable.Buffer()
          if (snpIndex.nonEmpty) {
            snp = snps.slice(snpIndex.min, snpIndex.max + 1).map(y => "SNP\t" + y)
          }

          if (indelIndex.nonEmpty) {
            indel = indels.slice(indelIndex.min, indelIndex.max + 1).map(y => "Indel\t" + y)
          }

          if (svIndex.nonEmpty) {
            sv = svs.slice(svIndex.min, svIndex.max + 1).map(y => "SV\t" + y)
          }

          FileUtils.writeLines(s"$path/gene/${x.geneid}.txt".toFile, (snp ++ indel ++ sv).asJava)
        }

      }


    }
    println("success")

  }


  def getSnpFreq = {
    val path = "H:\\紫花苜蓿数据库\\2023_1_9\\变异"

    genomeDao.getAllGenome.toAwait.filter(_.species == "ZMNO").groupBy(_.chr).foreach { z =>
      val chr = z._1
      println(chr)

      val snps = s"$path/snp_freq/$chr.txt".readLines
      val snpIndexs = s"$path/snp_freq_index/$chr.txt".readLines.map(_.toInt)

      println("text")
      z._2.foreach { x =>
        val start = x.start
        val end = x.end
        val snpIndex = snpIndexs.filter(y => y >= start && y <= end)


        if (snpIndex.nonEmpty) {
          var snp = snps.slice(snpIndex.min, snpIndex.max + 1)
          FileUtils.writeLines(s"$path/gene/${x.geneid}.txt".toFile, snp.asJava)
        }
      }

    }

    println("success")

  }


  def getNetWork1 = {
    val net = "H:\\紫花苜蓿数据库\\2022_12_19\\Cytoscape/CytoscapeInput.txt".readLines
    genomeDao.getAllGenome.toAwait.filter(_.species == "ZMNO").groupBy(_.chr).foreach {
      z =>
        z._2.foreach {
          x =>
            val filter1 = net.filter(y => y.contains(x.geneid))
            if (filter1.nonEmpty) {
              //  val genes = filter1.map(_.split("\t").take(2).filter(_ != x.geneid).head)
              /*        val filter2 = genes.flatMap { i =>
                        genes.map { j =>
                          net.filter(y => y.indexOf(i) != -1 && y.indexOf(j) != -1)
                        }
                      }*/
              FileUtils.writeLines(s"H:\\紫花苜蓿数据库\\2022_12_19\\Cytoscape/network/${
                x.geneid
              }.txt".toFile, (filter1).asJava)
            }
        }
    }

    println("success")

  }


  def getNetWork2 = {
    "H:\\紫花苜蓿数据库\\2022_12_19\\Cytoscape\\network".listFile.foreach {
      x =>
        val lines = x.readLines.sortBy(_.split("\t")(2).toDouble).reverse.take(20)
        val geneName = x.getName.dropRight(4)
        val genes = lines.map(_.split("\t").take(2).find(_ != geneName).get)
        val others = genes.zipWithIndex.flatMap {
          case (y, i) =>
            val f = s"H:\\紫花苜蓿数据库\\2022_12_19\\Cytoscape\\network/$y.txt".readLines.filter(_.split("\t")(2).toDouble >= 0.5)
            genes.drop(i + 1).flatMap {
              z =>
                f.filter(_.contains(z))
            }
        }.distinct


        FileUtils.writeLines(s"H:\\紫花苜蓿数据库\\2022_12_19\\Cytoscape\\network2/${
          x.getName
        }".toFile, (lines ++ others).asJava)
    }
    println("success")

  }

  def getBlockRange = {
    val genome = genomeDao.getAllGenome.toAwait.map(x => x.geneid -> (x.chr, x.start.toInt, x.end.toInt, x.species)).toMap

    val len = s"${Global.path}/blockLen".listFile.map { x =>

        val name = x.getName.dropRight(4)
        name -> x.readLines.map(_.split("\t")).zipWithIndex.map(y => (y._1.head -> (y._1.last, y._2))).toMap


    }.toMap

    "H:\\紫花苜蓿数据库\\collinearity".listFile.foreach { x =>
      val name = x.getName.split('.').init.mkString(".").toUpperCase

      println(name)
      val row = x.readFileToString.split("###").tail.zipWithIndex.map { case (y, i) =>
        val lines = y.trim.split("\n")
        val genes = lines.tail.map(_.split("\t")).map { z => z(0).split('.').init.mkString(".") -> z(1).split('.').init.mkString(".") }
        val g = genes.map(_._1).map { z => genome(z) }
        val gChr = g.map(_._1).head

        val gS = g.head._4

        val r = genes.map(_._2).map { z => genome(z) }
        val rChr = r.map(_._1).head

        val rS = r.head._4

        val gs1 = gS
        val rs1 = rS

        val s1 = len(gs1)
        val s2 = len(rs1)

        if (s1.contains(gChr) && s2.contains(rChr)) {
          val n1 = s1(gChr)
          val len1 = n1._1.toDouble
          val num1 = n1._2.toDouble

          val n2 = s2(rChr)
          val len2 = n2._1.toDouble
          val num2 = n2._2.toDouble

          val datas = genes.map { z =>
            val g1 = genome(z._1)
            val g2 = genome(z._2)
            val pos1 = (g1._3.toDouble + Math.abs((g1._3.toDouble - g1._2.toDouble) / 2)) / len1 + num1
            val pos2 = (g2._3.toDouble + Math.abs((g2._3.toDouble - g2._2.toDouble) / 2)) / len2 + num2
            (pos1, pos2, z._1 + "(" + g1._1 + ":" + g1._2 + "-" + g1._3 + ")", z._2 + "(" + g2._1 + ":" + g2._2 + "-" + g2._3 + ")")
          }

          val json = Json.obj("name" -> (gs1 + "_vs_" + rs1 + "_" + (i + 1)), "data" -> datas.map(x => (x._1, x._2)))

          val pos = datas.map { z =>
            (z._1 + "-" + z._2) -> (z._3 + "\t" + z._4)
          }.toMap

          (json, pos)
        } else {
          (Json.obj("" -> ""), Map("false" -> "false"))
        }
      }.filter(_._2.head._1 != "false")

      val js = Json.toJson(row.map(_._1))
      val poss = Json.toJson(row.flatMap(_._2).toMap)
      FileUtils.writeStringToFile(s"I:\\alfalfa_db\\config\\synteny/${name}.json".toFile, js.toString)
      FileUtils.writeStringToFile(s"I:\\alfalfa_db\\config\\synteny/${name}.pos".toFile, poss.toString)

      println("success")
    }

  }


}
